﻿// <copyright file="BubblePointsDataView.cs" company="Oleg V. Polikarpotchkin">
// Copyright © 2008-2009 Oleg V. Polikarpotchkin. All Right Reserved
// </copyright>
// <author>Oleg V. Polikarpotchkin</author>
// <email>ov-p@yandex.ru</email>
// <date>2009-04-18</date>
// <summary>OpenWPFChart library. BubblePointsDataView is the ItemDataView with the DataScale in the z direction 
// and the Bubble related stuff.</summary>
// <revision>$Id$</revision>

using System;
using System.ComponentModel;
using System.Text;
using System.Windows;
using System.Windows.Media;

namespace OpenWPFChart.Parts
{
	/// <summary>
	/// BubblePointsDataView is the ItemDataView with the DataScale in the z direction 
	/// and the Bubble related stuff.
	/// </summary>
	public class BubblePointsDataView : ItemDataView, IBubble
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="BubblePointsDataView"/> class.
		/// </summary>
		/// <remarks>
		/// This object default VisualCue is the <see cref="BubblesVisual"/> Type.
		/// </remarks>
		public BubblePointsDataView()
		{
			base.VisualCue = typeof(BubblesVisual);
			bubble = new GeometryDrawing(Brushes.BurlyWood, new Pen(Brushes.Red, 1)
					, new EllipseGeometry(new Point(0, 0), 4, 4));
			bubble.Changed += BubbleChanged;
			labelBrush = Brushes.Black.Clone() as Brush;
			labelBrush.Changed += LabelBrushChanged;
		}

		#region VisualCue
		/// <summary>
		/// Gets or sets the VisualCue property.
		/// </summary>
		/// <remremarks>
		/// Forbids this object VisualCue property change.
		/// </remremarks>
		public override object VisualCue
		{
			get { return base.VisualCue; }
			set	{ }
		}
		#endregion VisualCue

		#region ZScale
		ChartScale zScale;
		/// <summary>
		/// Gets or sets the ZScale property.
		/// </summary>
		public ChartScale ZScale
		{
			get { return zScale; }
			set
			{
				if (zScale != value)
				{
					if (value != null)
					{
						if (!value.IsConsistent)
							throw new ArgumentException("New ZScale value isn't in consistent state");
						if (ItemData != null && !value.CompatibleWith(ItemData.GetDimensionType(2)))
							throw new ArgumentException("New ZScale value type doesn't match the one of data type");
					}

					if (zScale != null)
						zScale.PropertyChanged -= ZScaleChanged;

					zScale = value;

					if (zScale != null)
						zScale.PropertyChanged += ZScaleChanged;

					NotifyPropertyChanged("ZScale");
				}
			}
		}
		/// <summary>
		/// ZScale PropertyChanged event handler.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
		private void ZScaleChanged(object sender, PropertyChangedEventArgs e)
		{
			NotifyPropertyChanged("ZScale." + e.PropertyName);
		}
		#endregion ZScale

		#region Bubble Drawing
		Drawing bubble;
		/// <summary>
		/// Gets or sets the Bubble Drawing.
		/// </summary>
		/// <value>any Drawing</value>
		public Drawing Bubble
		{
			get { return bubble; }
			set
			{
				if (bubble != value)
				{
					if (bubble != null && !bubble.IsFrozen)
						bubble.Changed -= BubbleChanged;
					bubble = value;
					if (bubble != null && !bubble.IsFrozen)
						bubble.Changed += BubbleChanged;
					NotifyPropertyChanged("Bubble");
				}
			}
		}

		/// <summary>
		/// Bubble changed.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		void BubbleChanged(object sender, EventArgs e)
		{
			NotifyPropertyChanged("Bubble");
		}
		#endregion Bubble Drawing

		#region Label
		BubbleLabelAlignment labelAlignment = BubbleLabelAlignment.Center;
		/// <summary>
		/// Gets or sets the value defining how the Label is aligned relative to its Bubble.
		/// </summary>
		/// <remarks>To hide the Label set this property to <see cref="BubbleLabelAlignment.DontShow"/> value.</remarks>
		public BubbleLabelAlignment LabelAlignment
		{
			get { return labelAlignment; }
			set
			{
				if (labelAlignment != value)
				{
					labelAlignment = value;
					NotifyPropertyChanged("LabelAlignment");
				}
			}
		}

		bool scaleLabelToBubbleSize;
		/// <summary>
		/// Gets or sets the ScaleLabelToBubbleSize property.
		/// </summary>
		/// <remarks>If <c>true</c> the label is scaled to fit into the Bubble.</remarks>
		public bool ScaleLabelToBubbleSize
		{
			get { return scaleLabelToBubbleSize; }
			set
			{
				if (scaleLabelToBubbleSize != value)
				{
					scaleLabelToBubbleSize = value;
					NotifyPropertyChanged("ScaleLabelToBubbleSize");
				}
			}
		}

		string labelFormat = "G";
		/// <summary>
		/// Gets or sets the LabelFormat property.
		/// </summary>
		/// <remarks>Standard format string.</remarks>
		public string LabelFormat
		{
			get { return labelFormat; }
			set
			{
				if (labelFormat != value)
				{
					labelFormat = value;
					NotifyPropertyChanged("LabelFormat");
				}
			}
		}

		#region LabelBrush
		Brush labelBrush;
		/// <summary>
		/// Gets or sets the LabelBrush property.
		/// </summary>
		public Brush LabelBrush
		{
			get { return labelBrush; }
			set
			{
				if (labelBrush != value)
				{
					if (labelBrush != null && !labelBrush.IsFrozen)
						labelBrush.Changed -= LabelBrushChanged;
					labelBrush = value;
					if (labelBrush != null && !labelBrush.IsFrozen)
						labelBrush.Changed += LabelBrushChanged;
					NotifyPropertyChanged("LabelBrush");
				}
			}
		}

		/// <summary>
		/// LabelBrush changed.
		/// </summary>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		void LabelBrushChanged(object sender, EventArgs e)
		{
			NotifyPropertyChanged("LabelBrush");
		}
		#endregion LabelBrush

		#region Label Font
		FontFamily fontFamily = SystemFonts.MessageFontFamily;
		/// <summary>
		/// Gets or sets the FontFamily property.
		/// </summary>
		/// <remarks>Used to draw bubble labels.</remarks>
		public FontFamily FontFamily
		{
			get { return fontFamily; }
			set
			{
				if (fontFamily != value)
				{
					fontFamily = value;
					NotifyPropertyChanged("FontFamily");
				}
			}
		}

		double fontSize = 8.0;
		/// <summary>
		/// Gets or sets the FontSize property.
		/// </summary>
		/// <remarks>Used to draw bubble labels.</remarks>
		public double FontSize
		{
			get { return fontSize; }
			set
			{
				if (fontSize != value)
				{
					fontSize = value;
					NotifyPropertyChanged("FontSize");
				}
			}
		}

		FontStretch fontStretch = FontStretches.Normal;
		/// <summary>
		/// Gets or sets the FontStretch property.
		/// </summary>
		/// <remarks>Used to draw bubble labels.</remarks>
		public FontStretch FontStretch
		{
			get { return fontStretch; }
			set
			{
				if (fontStretch != value)
				{
					fontStretch = value;
					NotifyPropertyChanged("FontStretch");
				}
			}
		}

		FontStyle fontStyle = SystemFonts.MessageFontStyle;
		/// <summary>
		/// Gets or sets the FontStyle property.
		/// </summary>
		/// <remarks>Used to draw bubble labels.</remarks>
		public FontStyle FontStyle
		{
			get { return fontStyle; }
			set
			{
				if (fontStyle != value)
				{
					fontStyle = value;
					NotifyPropertyChanged("FontStyle");
				}
			}
		}

		FontWeight fontWeight = SystemFonts.MessageFontWeight;
		/// <summary>
		/// Gets or sets the FontWeight property.
		/// </summary>
		/// <remarks>Used to draw bubble labels.</remarks>
		public FontWeight FontWeight
		{
			get { return fontWeight; }
			set
			{
				if (fontWeight != value)
				{
					fontWeight = value;
					NotifyPropertyChanged("FontWeight");
				}
			}
		}
		#endregion Label Font
		#endregion Label

		#region ICloneable Override
		/// <summary>
		/// Creates a new object that is a copy of the current instance.
		/// </summary>
		/// <returns>
		/// A new object that is a copy of this instance.
		/// </returns>
		/// <remarks>This method is designed to provide the deep cloning facility if desired.</remarks>
		protected override object CloneInternal()
		{
			BubblePointsDataView clone = base.CloneInternal() as BubblePointsDataView;
			// Clone ZScale.
			clone.ZScale = clone.ZScale.Clone() as ChartScale;
			clone.zScale.PropertyChanged += ZScaleChanged;
			// Bubble.
			if (bubble != null)
			{
				clone.bubble = bubble.Clone();
				clone.bubble.Changed += clone.BubbleChanged;
			}
			// LabelBrush.
			if (labelBrush != null)
			{
				clone.labelBrush = labelBrush.Clone();
				clone.labelBrush.Changed += clone.LabelBrushChanged;
			}
			return clone;
		}
		#endregion ICloneable Override

		#region IDataErrorInfo Overrides
		/// <inheritdoc />
		protected override string ObjectErrorInfo
		{
			get
			{
				StringBuilder sb = new StringBuilder(base.ObjectErrorInfo);
				string s = base.GetDataErrorInfo("ZScale");
				if (!string.IsNullOrEmpty(s))
				{
					if (sb.Length > 0)
						sb.Append(Environment.NewLine);
					sb.Append(s);
				}
				s = base.GetDataErrorInfo("LabelFormat");
				if (!string.IsNullOrEmpty(s))
				{
					if (sb.Length > 0)
						sb.Append(Environment.NewLine);
					sb.Append(s);
				}
				if (sb.Length > 0)
					return sb.ToString();
				return null;
			}
		}

		/// <inheritdoc />
		protected override string GetDataErrorInfo(string columnName)
		{
			switch (columnName)
			{
				case "ZScale":
					return (ZScale as IDataErrorInfo).Error;
				case "LabelFormat":
					return string.IsNullOrEmpty(LabelFormat) ? "LabelFormat must not be empty" : null;
			}
			return base.GetDataErrorInfo(columnName);
		}
		#endregion IDataErrorInfo Overrides
	}
}
